﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using blackhouse.BlackApart.Data;
using System.Globalization;

namespace blackhouse.BlackApart.Business.Opinions {
    public class StatisticsOpinion {

        #region Fields

        private IOpinionsDataAccess iOpinionsDataAccess;
        private StatisticOpinionData[] data;
        private int apartmentTypeFiltered = 0;

        #endregion

        #region Constructors

        internal StatisticsOpinion(Data.IOpinionsDataAccess iOpinionsDataAccess) {
            this.iOpinionsDataAccess = iOpinionsDataAccess;
        }

        #endregion

        #region Methods

        public void SetFilterByApartmentType(int apartmentTypeId) {
            if (this.apartmentTypeFiltered != apartmentTypeId) {
                this.data = null;
                this.data = this.GetData().Where(s => s.ApartmentTypeId == apartmentTypeId).ToArray();
            }
        }

        private StatisticOpinionData[] GetData() {
            if (this.data == null)
                this.data = this.iOpinionsDataAccess.GetStatisticOpinionData();
            return this.data;
        }

        public float GetAverageRating() {
            if (this.GetData().Length == 0) return -1;
            return this.GetData().Average(s => s.Value);
        }

        public string GetAverageRatingAsText() {
            float rating = this.GetAverageRating();
            if (rating > 0)
                return rating.ToString("F2");
            else
                return " - ";
        }

        public float GetAverageRating(int apartmentTypeId) {
            this.SetFilterByApartmentType(0); // prevent, jeżeli chcielibyśmy najpierw ustawić typ, a później filtrować po innym typie.
            IEnumerable<StatisticOpinionData> data = this.GetData().Where(s => s.ApartmentTypeId == apartmentTypeId);
            if (data.Count() == 0) return -1;
            return data.Average(s => s.Value);
        }

        public string GetAverageRatingAsText(int apartmentTypeId) {
            float rating = this.GetAverageRating(apartmentTypeId);
            if (rating > 0)
                return rating.ToString("F2");
            else
                return " - ";
        }

        public string GetAverageRatingText() {
            float rating = this.GetAverageRating();
            return this.GetAverageText(rating);
        }

        public int BasedOnNumberOfOpinions() {
            return this.GetData().Select(so => so.OpinionId).Distinct().Count();
        }

        public int BasedOnNumberOfOpinions(int apartmentTypeId) {
            this.SetFilterByApartmentType(0); // prevent, jeżeli chcielibyśmy najpierw ustawić typ, a później filtrować po innym typie.
            return this.GetData().Where(sod => sod.ApartmentTypeId == apartmentTypeId).Select(so => so.OpinionId).Distinct().Count();
        }

        public float GetCategoryAverageRating(int categoryId) {
            var d = this.GetData().Where(s => s.OpinionCategoryId == categoryId);
            if (d.Count() == 0) return -1;
            else
                return d.Average(s => s.Value);
        }

        public string GetCategoryAverageRatingAsText(int categoryId) {
            float rating = this.GetCategoryAverageRating(categoryId);
            if (rating > 0)
                return rating.ToString("F2");
            else
                return " - ";
        }

        public string GetCategoryAverageRatingText(int categoryId) {
            float rating = this.GetCategoryAverageRating(categoryId);
            if (rating > 0)
                return this.GetAverageText(rating);
            else
                return this.GetAverageText(-1);
        }

        private string GetAverageText(float averageValue) {

            OpinionTextData[] data = this.iOpinionsDataAccess.GetOpinionTextValues(CultureInfo.CurrentCulture.Name);

            OpinionTextData searchText;
            averageValue = averageValue * 10;

            if (averageValue > 100)
                searchText = data.OrderByDescending(ot => ot.PercentTrigger).FirstOrDefault();
            else if (averageValue <= 0)
                searchText = data.OrderBy(ot => ot.PercentTrigger).FirstOrDefault();
            else
                searchText = data.Where(ot => ot.PercentTrigger <= averageValue).OrderBy(ot => averageValue - ot.PercentTrigger).FirstOrDefault();

            return searchText.Text;
        }

        public IOpinion GetMostValuableOpinion(int apartmentTypeId, TimeSpan messagesNotOlderThan, string language) {
            IEnumerable<StatisticOpinionData> sod = this.GetData().Where(s => s.ApartmentTypeId == apartmentTypeId && s.PublishedDate.Add(messagesNotOlderThan) > DateTime.Now).OrderByDescending(s => s.Value);

            StatisticOpinionData languageSod = sod.FirstOrDefault(s => s.Language == language);

            if (languageSod.ApartmentTypeId == apartmentTypeId)
                return Factory.Instance().GetOpinion(languageSod.OpinionId);

            StatisticOpinionData invariantSod = sod.FirstOrDefault();

            if (invariantSod.ApartmentTypeId == apartmentTypeId)
                return Factory.Instance().GetOpinion(invariantSod.OpinionId);
            else return null;
        }

        /// <summary>
        /// Pobiera opinie na podstawie których zostały stworzone statystyki.
        /// </summary>
        /// <returns></returns>
        public IOpinion[] GetOpinions() {
            IOpinion[] opinions = Factory.Instance().GetOpinion().GetOpinions();
            var q = from o in opinions
                    join d in this.GetData() on o.Id equals d.OpinionId
                    select o;
            return q.Distinct().OrderByDescending(o => o.PublishedDate).ToArray();
        }

        public float GetSimilarAverageRating(int opinionId, TimeSpan timeWatch) {
            int cpAT = this.apartmentTypeFiltered;
            this.SetFilterByApartmentType(0);
            int at = (from d in this.GetData() where d.OpinionId == opinionId select d.ApartmentTypeId).FirstOrDefault();
            float value = -1;
            if (at != 0) {
                IEnumerable<StatisticOpinionData> data = this.GetData().Where(s => s.ApartmentTypeId == at);
                if (data.Count() > 0)
                    value = (float)Math.Round((float)data.Average(s => s.Value), 2);
            }
            if (cpAT != this.apartmentTypeFiltered)
                this.SetFilterByApartmentType(cpAT);
            return value;
        }

        #endregion

    }
}
